package luadb

import (
	"strings"

	lua "github.com/yuin/gopher-lua"
	"gorm.io/gorm"
)

//Execer .
type Execer interface {
	Exec(sql string, Args ...interface{}) (db *gorm.DB)
}

// //Queryer .
type Queryer interface {
	//QueryRows(sqlOrArgs ...interface{}) (*sql.Rows, error)
	Raw(sql string, values ...interface{}) (tx *gorm.DB)
}

//Exec 执行SQL语句
func Exec(l *lua.LState) int {
	n := l.GetTop()
	if n < 1 {
		l.Push(lua.LString("Parameter error"))
		return 1
	}
	db := "default"
	sql := l.CheckString(1)
	if n > 1 {
		db = sql
		sql = l.CheckString(2)
	}
	res, err := boss.Exec(db, sql)
	l.Push(lua.LNumber(res))
	if err != nil {
		l.Push(lua.LString(err.Error()))
		return 2
	}

	return 1
}

func genPrepareSQL(tableName string, table *lua.LTable) (sql string, inputNum int) {
	cols := table.RawGet(lua.LNumber(0)).String()
	sql = "INSERT INTO " + tableName + " (" + cols + ") VALUES ("
	inputNum = len(strings.Split(cols, ","))
	params := strings.Repeat("?,", inputNum)
	params = strings.TrimSuffix(params, ",")
	sql += params + ")"
	return
}

func execInsert(db Execer, l *lua.LState, tableName string, table *lua.LTable) int {
	sql, inputNum := genPrepareSQL(tableName, table)

	fields := make([]interface{}, inputNum)
	for i := range fields {
		val := table.RawGetInt(i)
		if val == lua.LNil {
			fields[i] = nil
			continue
		}
		fields[i] = val
	}

	sdb := db.Exec(sql, fields...)
	err := sdb.Error
	res := sdb.RowsAffected
	l.Push(lua.LNumber(res))
	if err != nil {
		l.Push(lua.LString(err.Error()))
		return 2
	}
	return 1
}

//Insert .
func Insert(l *lua.LState) int {
	n := l.GetTop()
	if n < 2 {
		l.Push(lua.LString("Parameter error"))
		return 1
	}
	db := "default"
	tableName := l.CheckString(1)
	var table *lua.LTable
	if n > 2 {
		db = tableName
		tableName = l.CheckString(2)
		table = l.CheckTable(3)
	} else {
		table = l.CheckTable(2)
	}

	engine, ok := boss.dbs[db]
	if !ok {
		l.Push(lua.LString("No this database " + db))
		return 1
	}

	return execInsert(engine, l, tableName, table)
}

//DataTable .
func DataTable(l *lua.LState) int {
	n := l.GetTop()
	if n < 1 {
		l.Push(lua.LString("Paramster error"))
		return 1
	}
	db := "default"
	sql := l.CheckString(1)
	if n > 1 {
		db = sql
		sql = l.CheckString(2)
	}

	mdb, ok := boss.dbs[db]
	if !ok {
		l.Push(lua.LString("No this database:" + db))
		return 1
	}

	rows, err := mdb.Raw(sql).Rows() //sess.QueryRows(sql)
	if err != nil {
		l.Push(lua.LString(err.Error()))
		return 1
	}
	if rows == nil {
		l.Push(lua.LString("No data"))
		return 1
	}
	defer rows.Close()
	tab := l.NewTable()
	fields, err := rows.Columns()
	if err != nil {
		l.Push(lua.LString(err.Error()))
		return 1
	}
	tab.Insert(0, lua.LString(strings.Join(fields, ",")))
	count := 1
	for rows.Next() {
		row := l.NewTable()
		err = row2table(rows, row, false)
		if err != nil {
			l.Push(lua.LString(err.Error()))
			return 1
		}
		tab.Insert(count, row)
		count++
	}

	l.Push(tab)
	return 1
}
